<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
               "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
lang="en" xml:lang="en">
<head>
<title>Writing AxsJAX Scripts</title>
<meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1"/>
<meta name="generator" content="Org-mode"/>
<meta name="generated" content="2008/07/03 15:15:54"/>
<meta name="author" content="T.V. Raman, Charles L. Chen"/>
<style type="text/css">
  html {
	font-family: Times, serif;
	font-size: 12pt;
  }
  .title { text-align: center; }
  .todo  { color: red; }
  .done { color: green; }
  .timestamp { color: grey }
  .timestamp-kwd { color: CadetBlue }
  .tag { background-color:lightblue; font-weight:normal }
  .target { }
  pre {
	border: 1pt solid #AEBDCC;
	background-color: #F3F5F7;
	padding: 5pt;
	font-family: courier, monospace;
        font-size: 90%;
  }
  table { border-collapse: collapse; }
  td, th {
	vertical-align: top;
	<!--border: 1pt solid #ADB9CC;-->
  }
  dt { font-weight: bold; }
</style>
</head><body>
<h1 class="title">Writing AxsJAX Scripts</h1>
<div id="table-of-contents">
<h2>Table of Contents</h2>
<div id="text-table-of-contents">
<ul>
<li><a href="#sec-1">1 Setting Up Your Development Environment</a></li>
<li><a href="#sec-2">2 Applying AxsJAX to Content Rich Sites</a></li>
<li><a href="#sec-3">3 Example: Enhancing Google Product Search Via AxsJAX</a>
<ul>
<li><a href="#sec-3.1">3.1 Writing the CNR file</a></li>
<li><a href="#sec-3.2">3.2 Putting the CNR file into the JS file.</a></li>
<li><a href="#sec-3.3">3.3 Load up Google Product Search and do a search.</a></li>
<li><a href="#sec-3.4">3.4 Add custom functions to improve the behavior of the page.</a></li>
<li><a href="#sec-3.5">3.5 Wrapping Things Up </a></li>
</ul>
</li>
<li><a href="#sec-4">4 Applying AxsJAX to Highly Interactive Web Applications</a></li>
<li><a href="#sec-5">5 Example: Enhancing Google Reader Via AxsJAX   </a>
<ul>
<li><a href="#sec-5.1">5.1 Making the app keyboard usable</a></li>
<li><a href="#sec-5.2">5.2 Writing a CNR if necessary</a></li>
<li><a href="#sec-5.3">5.3 Speaking the result of navigating the feeds</a></li>
<li><a href="#sec-5.4">5.4 Speaking the result of user actions</a></li>
<li><a href="#sec-5.5">5.5 Load up Google Reader and try it out</a></li>
<li><a href="#sec-5.6">5.6 Putting on the finishing touches</a></li>
<li><a href="#sec-5.7">5.7 Wrapping Things Up </a></li>
</ul>
</li>
<li><a href="#sec-6">6 References</a></li>
</ul>
</div>
</div>

<div id="outline-container-1" class="outline-2">
<h2 id="sec-1">1 Setting Up Your Development Environment</h2>
<div id="text-1">


<p>
While there are many ways to set up your development environment for working 
with AxsJAX, this tutorial will cover the easiest way to setup and get going. 
You may substitute any of the tools here for ones that you are more comfortable 
with that have the same functionality, but this guide will assume that you are 
using the tools listed here.
</p>
<ol>
<li>
<a href="http://portableapps.com/apps/internet/firefox_portable">Get Portable Firefox.</a> This will give you a sandboxed version of Firefox with a clean profile so that you don't need to worry about conflicts with any existing extensions. 
</li>
<li>
Install the following extensions on your Portable Firefox:
<ul>
<li>
<a href="https://addons.mozilla.org/en-US/firefox/addon/748">Greasemonkey</a>  for injecting scripts.
</li>
<li>
<a href="https://addons.mozilla.org/en-US/firefox/addon/1192">XPather</a>  for interactively discovering XPath locators.
</li>
<li>
<a href="https://addons.mozilla.org/en-US/firefox/addon/1100">Event Spy</a>  for inspecting events as they happen.
</li>
<li>
<a href="http://www.firevox.clcworld.net/installation.html">Fire Vox</a>  for adding spoken output to Firefox.
</li>
</ul>
</li>
<li>
Download the <a href="http://www.server2go-web.de/download/download.html">"MICRO-PACKAGE" of Server2Go</a> and unzip it to your hard drive.
</li>
<li>
Save the <a href="http://google-axsjax.googlecode.com/svn/trunk/skel/axsSkel.js">axsSkel.js</a> file under the "htdocs" directory of where you unzipped Server2Go in the previous step.
</li>
<li>
Start Server2Go. You will get a browser window with a URL that looks like "<a href="http://127.0.0.1:4001/"">http://127.0.0.1:4001/"</a> and a page that says you are running Server2Go successfully. Make a note of what your URL is.
</li>
<li>
Install <a href="http://google-axsjax.googlecode.com/svn/trunk/skel/axsSkelLoader.user.js">axsSkelLoader.user.js</a> as a Greasemonkey script. By default this will run on all pages; you may want to change this to only run on the page that you will be working on by going to Tools &gt; Greasemonkey &gt; Manage User Scripts and editing the "Included Pages" setting to just be your target page.
</li>
<li>
Go to Tools &gt; Greasemonkey &gt; Manage User Scripts and select "axsSkelLoader". Click "Edit". Look at the line that reads:
<pre>
 /* Modify this line to match where you have the script that you are working on */
 myScript.src = 'http://127.0.0.1:4001/axsSkel.js';
</pre>
and verify that this URL is indeed what your URL was in step 5. If not, change it to match and save the file.
</li>
<li>
Go to the site you wish to apply AxsJAX to. You should get an alert box saying: "AxsSkel loaded and initialized!"



</li>
</ol>
</div>

</div>

<div id="outline-container-2" class="outline-2">
<h2 id="sec-2">2 Applying AxsJAX to Content Rich Sites</h2>
<div id="text-2">

<ol>
<li>
Write a <a href="http://google-axsjax.googlecode.com/svn/trunk/docs/cnr.html">Content Navigation Rule (CNR)</a> for the site. <a href="http://google-axsjax.googlecode.com/svn/trunk/skel/skel_cnr.xml">Here is a skeleton CNR file to help you get started.</a> An easy way to
generate the XPath expressions is to use the DOM Inspector in
Firefox with XPather. DOM Inspector allows you to
interactively select nodes in the DOM; XPather shows you the
XPath expression that would locate that selection. This will
give you an initial xpath expression that you can then
simplify and generalize.

</li>
<li>
Replace this line:
<pre>
var cnrString = "PUT THE CNR XML HERE";
</pre>
in your axsSkel.js file with the CNR that you wrote in the previous step.
<a href="http://google-axsjax.googlecode.com/svn/trunk/docs/tools/cnr_to_js.html">You can get a copy-paste friendly string by using this tool.</a>

</li>
<li>
Try it out! At this point, the navigation should be working and you should 
be able to easily get to all the content on the page. If there is important 
content that you cannot reach, make sure that you are really including it in 
your xpath expressions. As in step 1, XPather and DOM Inspector are 
indispensable.

</li>
<li>
If there is more functionality that needs to be enabled, you can augment 
the axsSkel.js file with your own functions that are tailored for the site. 

</li>
</ol>
</div>

</div>

<div id="outline-container-3" class="outline-2">
<h2 id="sec-3">3 Example: Enhancing Google Product Search Via AxsJAX</h2>
<div id="text-3">



</div>

<div id="outline-container-3.1" class="outline-3">
<h3 id="sec-3.1">3.1 Writing the CNR file</h3>
<div id="text-3.1">


<p>
The main trail here is the list of items returned by Product
Search. Let's try to find the xpath for those items. Use DOM
Inspector and click on the first result. That will select a node
in the DOM tree. If that selection is too narrow, move up the
tree until you select more. When you are satisfied with what the
flashing rectangle has selected, note the XPath expression that
you get.
</p>
<p>
<img src="http://google-axsjax.googlecode.com/svn/trunk/docs/tutorial/images/xpath_00.png"/>
</p>
<p>
Now use DOM Inspector and click on the second result. Notice that the second 
result's xpath is quite similar to the first result's.
</p>
<p>
<img src="http://google-axsjax.googlecode.com/svn/trunk/docs/tutorial/images/xpath_01.png"/>
</p>
<p>
The first xpath is:
<pre>
 /html/body/table[3]/tbody/tr[2]
</pre>
and the second is:
<pre>
 /html/body/table[3]/tbody/tr[3]
</pre>
</p>
<p>
Based on that, a good guess for the xpath for all the results would be:
<pre>
 /html/body/table[3]/tbody/tr[*]
</pre>
Let's put that into XPather and see what we get.
</p>
<p>
<img src="http://google-axsjax.googlecode.com/svn/trunk/docs/tutorial/images/xpath_02.png"/>
</p>
<p>
This is pretty close to what we want, but we are getting an extra node at the 
front. Notice that this extra node has a class name.
To exclude it, let's change the xpath to only include nodes without class"
<pre>
 /html/body/table[3]/tbody/tr[not(@class)]
</pre>
</p>
<p>
<img src="http://google-axsjax.googlecode.com/svn/trunk/docs/tutorial/images/xpath_03.png"/>
</p>
<p>
Success! Now we only have the search results. 
</p>
<p>
At this point, all we have to do is decide on the navigation keys we want to use and then 
write the CNR.
</p>
<p>
<a href="http://google-axsjax.googlecode.com/svn/trunk/docs/tutorial/productSearch_cnr_00.xml">Here's the CNR file that we get.</a>
</p>
</div>

</div>

<div id="outline-container-3.2" class="outline-3">
<h3 id="sec-3.2">3.2 Putting the CNR file into the JS file.</h3>
<div id="text-3.2">

<p>This step is pretty easy. Open up your axsSkel.js file and put in the CNR 
string.
</p>
<p>
<a href="http://google-axsjax.googlecode.com/svn/trunk/docs/tutorial/axsProductSearch_00.js">This is the JS file that we get.</a>
</p>
</div>

</div>

<div id="outline-container-3.3" class="outline-3">
<h3 id="sec-3.3">3.3 Load up <a href="http://www.google.com/products">Google Product Search</a> and do a search.</h3>
<div id="text-3.3">

<p>Ok, not bad for a first attempt. It mostly works, but there are
definitely some areas for improvement. For starters, the user
isn't taken to the next page when they reach the end of a page of
results and try to go forward. Similarly, they should be taken to
the previous page if they reach the first result and try to go
backward. Let's add targets with "listTail"/"listHead" triggers
to address this. Just like in the first step, we use DOM
Inspector and XPather to discover what the xpaths for the
next/previous page links are. Also, the user should be able to
just hit ENTER to go to the result, so let's add a target with
ENTER as a hotkey for that as well.
</p>
<p>
<a href="http://google-axsjax.googlecode.com/svn/trunk/docs/tutorial/productSearch_cnr_01.xml">Here's the CNR file that we get.</a>
</p>
</div>

</div>

<div id="outline-container-3.4" class="outline-3">
<h3 id="sec-3.4">3.4 Add custom functions to improve the behavior of the page.</h3>
<div id="text-3.4">


<p>
Now that we can easily go through the results, is there anything
else that we can do to improve this page? As a matter of fact,
there is! Listening closely to what the results sound like, we
can hear that the spoken feedback is not exactly what we would
like. For instance, the first image of the result has no
alt text and some AT will read the entire URL. That image should
just be omitted as there is no reason for the user to hear
that. Also, the star rating is repeated since each star has an
alt tag that says how many stars there are, and the text of the
different parts of the result tend to run together. Let's write a
short JavaScript function that will process each result by adding
some smart formatting to it before speaking it. We can then use
the "action" attribute of the item element in our CNR to call our
function instead of using the default action of going to the node
and speaking it.
</p>
<p>
While we're at it, we should also add a function that causes the first result 
on a page to be spoken when the page is loaded.
</p>
<p>
Here's the code for reformatting the results to create a much better auditory 
user interface:
<pre>
 axsProductSearch.speakResult = function(item){
   var resultRow = item.elem;
   var title = axsProductSearch.getTitle(resultRow);
   var desc = axsProductSearch.getDesc(resultRow);
   var price = axsProductSearch.getPrice(resultRow);
   var seller = axsProductSearch.getSeller(resultRow);
   var ratings = axsProductSearch.getRatings(resultRow);
   var checkout = axsProductSearch.getCheckout(resultRow);
   
   var message = title + '. ' + 
                 price + '. ' +  
                         desc  +                               
                                 seller + '. ' +
                                 ratings +
                                 checkout;
   
   axsProductSearch.axsLensObj.view(resultRow);
   resultRow.scrollIntoView(true);
   axsProductSearch.axsJAXObj.speakTextViaNode(message);
 };
</pre>
</p>
<p>
<img src="http://google-axsjax.googlecode.com/svn/trunk/docs/tutorial/images/products_done.png"/>
</p>
<p>
Now, what the user hears for the above screen shot is:
</p>
<p>
Google e. encyclopedia. $29.99. In partnership with Google, DK presents the 
e.encyclopedia, a revolutionary approach to children's reference publishing. 
An illustrated general encyclopedia &hellip; Buy this at Adoremus Books. 64 seller 
ratings averages out to 5 stars. Accepts Google Checkout.
</p>
<p>
<a href="http://google-axsjax.googlecode.com/svn/trunk/docs/tutorial/axsProductSearch_01.js">Here is the JS file that we get at the end.</a>
</p>

</div>

</div>

<div id="outline-container-3.5" class="outline-3">
<h3 id="sec-3.5">3.5 Wrapping Things Up </h3>
<div id="text-3.5">

<p>This AxsJAX script for Product Search is basically done. Of course, 
there are a few things to polish up such as adding controls for search 
refinements. To see how we're evolving this script, please see the 
<a href="http://google-axsjax.googlecode.com/svn/trunk/productsearch">"productsearch" directory</a> 
in the AxsJAX project SVN repository.
</p>

</div>
</div>

</div>

<div id="outline-container-4" class="outline-2">
<h2 id="sec-4">4 Applying AxsJAX to Highly Interactive Web Applications</h2>
<div id="text-4">


<ol>
<li>
Make sure the app is keyboard usable. The goal is to make it possible to do 
common actions from the keyboard very quickly; users should never need to 
tab through multiple things on the page just to get at something. 

</li>
<li>
If there are static portions on this app that could benefit from CNR, follow 
the steps in the section above on <a href="#sec-2">Applying AxsJAX to Content Rich Sites</a>.
Otherwise, replace this line:
<pre>
var cnrString = "PUT THE CNR XML HERE";
</pre>
with
<pre>
var cnrString = "";
</pre>

</li>
<li>
Decide on an action whose result you wish to speak,
<ol>
<li>
Use DOM Inspector to find and select the node of the app that contains 
content which you know will change when you perform an action from the 
keyboard. 

</li>
<li>
Right-click on the node in the DOM tree and choose "Watch events&hellip;" to 
bring up Event Spy. Make sure that "Mutation" is checked and nothing else.

</li>
<li>
Perform the action from the keyboard and examine the event log to 
determine what events are fired when the action is performed.

</li>
<li>
Using this, write code for the axsSkel.nodeInsertedHandler or 
axsSkel.attrModifiedHandler that will speak the result of the event.

</li>
</ol>
</li>
<li>
Repeat the last step until all actions have their results spoken.

</li>
<li>
Try it out! Think of the different tasks that users will want to do with the 
the app. Is there a smooth workflow that can be done efficiently from the 
keyboard alone? Is there enough auditory feedback so that the application 
can be used without a monitor?

</li>
<li>
If there is more functionality that needs to be enabled, you can augment 
the axsSkel.js file with your own functions that are tailored for the site. 

</li>
</ol>
</div>

</div>

<div id="outline-container-5" class="outline-2">
<h2 id="sec-5">5 Example: Enhancing Google Reader Via AxsJAX   </h2>
<div id="text-5">



</div>

<div id="outline-container-5.1" class="outline-3">
<h3 id="sec-5.1">5.1 Making the app keyboard usable</h3>
<div id="text-5.1">


<p>
There's nothing to be done in this step since Google Reader is a power-user 
app that has keyboard shortcuts for pretty much everything. 
</p>
</div>

</div>

<div id="outline-container-5.2" class="outline-3">
<h3 id="sec-5.2">5.2 Writing a CNR if necessary</h3>
<div id="text-5.2">

<p>This step can also be skipped for Google Reader as the navigation through the 
feeds and articles is done entirely by Reader.
</p>
</div>

</div>

<div id="outline-container-5.3" class="outline-3">
<h3 id="sec-5.3">5.3 Speaking the result of navigating the feeds</h3>
<div id="text-5.3">

<p>Let's start by enabling spoken feedback for navigating the list of feeds. 
Google Reader provides Shift + n / Shift + p to navigate forward / backward in 
this list. When users do this, there is a highlight that indicates where they 
are as shown in the screen shot below.
<img src="http://google-axsjax.googlecode.com/svn/trunk/docs/tutorial/images/shift_n.png"/>
</p>
<p>
Now that we know what area is going to change, we can watch for changes in that 
area of the DOM using Event Spy.
</p>
<p>
First, start up DOM Inspector, find the area that will change, and right click 
on its node in the DOM tree. Then choose "Watch events&hellip;"
<img src="http://google-axsjax.googlecode.com/svn/trunk/docs/tutorial/images/watch_events_00.png"/>
</p>
<p>
Check the "Mutation" category, go back to the Reader, then hit Shift + n to 
navigate to the next feed. You will see that the Event Log now has captured a 
few events. 
<img src="http://google-axsjax.googlecode.com/svn/trunk/docs/tutorial/images/watch_events_01.png"/>
</p>
<p>
Uncheck the "Mutation" category so that you won't accidentally get more events. 
Let's explore this log to see what caused the highlighting effect to appear. 
It would make sense that something like this is done via CSS with classname 
changes, so we should focus our search on the DOM events which involve class.
After looking through the changes, we see something that looks quite promising. 
It is a DOMAttrModified event that has:
<pre>
 attrName = 'class'
 prevValue = 'link'
 newValue = 'link cursor'
</pre>
This is the type of change that we expected; it is a change to the classname, 
and the class name changes seem quite reasonable. The feed name was styled to 
look like a regular link before we navigated to it, and then afterwards, it 
still looked like a link but also acted as a cursor since it indicated our 
position. And finally, the target of this event points at "Cool Tools", 
which is the feed that we navigated to when we pressed Shift + n earlier.
</p>
<p>
With this information, we are ready to write a few lines of JavaScript to 
cause the user's assistive technology to speak when they navigate through the 
feeds. Since the event we are interested in is a DOMAttrModified event, we 
should modify axsSkel.attrModifiedHandler by adding a "if" condition that 
matches the events that are occurring with the event that we are interested in.
This is what the modified axsSkel.attrModifiedHandler looks like:
<pre>
axsSkel.attrModifiedHandler = function(evt){
  var attrib = evt.attrName;
  var newVal = evt.newValue;
  var oldVal = evt.prevValue;
  var target = evt.target;
  // If the target node is something that should
  // be spoken, speak it here.
  if ( (attrib == 'class') &amp;&amp; 
       (oldVal.indexOf('cursor') == -1) &amp;&amp;
       (newVal.indexOf('cursor') != -1) ){
    axsSkel.axsJAXObj.speakNode(target);
  }
};
</pre>
</p>
</div>

</div>

<div id="outline-container-5.4" class="outline-3">
<h3 id="sec-5.4">5.4 Speaking the result of user actions</h3>
<div id="text-5.4">

<p>We can repeat what we just did for speaking the result of navigating the feed 
list and do the same for other user actions. For example, reading the articles 
when the user navigates to them.
</p>
<p>
In this case, after looking at the Event Log, we find that the change is done 
by changing the ID of the article to "current-entry". Here is the code that we 
need to add to axsSkel.attrModifiedHandler:
<pre>
  if ( (attrib == 'id') &amp;&amp; 
       (oldVal.indexOf('current-entry') == -1) &amp;&amp;
       (newVal.indexOf('current-entry') != -1) ){
    axsSkel.axsJAXObj.speakNode(target);
  }  
</pre>
</p>
<p>
<a href="http://google-axsjax.googlecode.com/svn/trunk/docs/tutorial/axsReader_00.js">Here is the JS file that we get after providing speech feedback for navigating the feeds and their articles.</a>
</p>

</div>

</div>

<div id="outline-container-5.5" class="outline-3">
<h3 id="sec-5.5">5.5 Load up <a href="http://reader.google.com">Google Reader</a> and try it out</h3>
<div id="text-5.5">

<p>Just by adding two "if" statements to the axsSkel.attrModifiedHandler, we have 
dramatically improved the usability of Google Reader. Now, the user can use the 
built-in Reader shortcut keys to navigate the feeds and articles and hear 
spoken feedback. 
</p>
<p>
There are still a few improvements that we can make. For example, users should 
get feedback when the articles are first loaded. To sighted users looking at 
the screen, it is obvious when the articles have loaded, but to enable a good 
eyes-free experience, it is important to let users know when the loading has 
finished so that they know they can proceed.
</p>
<p>
Another needed improvement is to let users know when they have starred or 
unstarred an entry.
</p>
</div>

</div>

<div id="outline-container-5.6" class="outline-3">
<h3 id="sec-5.6">5.6 Putting on the finishing touches</h3>
<div id="text-5.6">

<p>We can make these two improvements quite easily by using the same techniques 
we used earlier. Here's the code that we need to add to 
axsSkel.attrModifiedHandler:
</p>
<p>
<pre>
 if( (target.id == 'viewer-box') &amp;&amp;
       (attrib == 'class') &amp;&amp;
       (oldVal.indexOf('hidden') != -1) &amp;&amp;
       (newVal.indexOf('hidden') == -1)  ){
     axsSkel.axsJAXObj.speakTextViaNode(axsSkel.ARTICLES_LOADED_STRING);
   }
   if( (attrib == 'class') &amp;&amp;
       (oldVal.indexOf('item-star-active') == -1) &amp;&amp;
         (newVal.indexOf('item-star-active') != -1) ){
     axsSkel.axsJAXObj.speakTextViaNode(axsSkel.ITEM_STARRED_STRING);
   }
   if( (attrib == 'class') &amp;&amp;
       (oldVal.indexOf('item-star-active') != -1) &amp;&amp;
         (newVal.indexOf('item-star-active') == -1) ){
     axsSkel.axsJAXObj.speakTextViaNode(axsSkel.ITEM_UNSTARRED_STRING);
   }
</pre>
</p>
<p>
<a href="http://google-axsjax.googlecode.com/svn/trunk/docs/tutorial/axsReader_01.js">Here is the JS file that we get at the end.</a>
</p>
</div>

</div>

<div id="outline-container-5.7" class="outline-3">
<h3 id="sec-5.7">5.7 Wrapping Things Up </h3>
<div id="text-5.7">

<p>This AxsJAX script for Google Reader now has most of the functionality that we 
want. Of course, there is still more to do. To see how we're evolving this 
script, please see the 
<a href="http://google-axsjax.googlecode.com/svn/trunk/reader">"reader" directory</a> 
in the AxsJAX project SVN repository. 
<a href="http://googleblog.blogspot.com/2008/03/aria-for-google-reader-in-praise-of.html">Also, Reader has already begun shipping its AxsJAX script as part of the product itself.</a>
</p>
</div>
</div>

</div>

<div id="outline-container-6" class="outline-2">
<h2 id="sec-6">6 References</h2>
<div id="text-6">


<p>
Here are useful references for additional reading:
</p>
<ul>
<li>
<a href="http://www.google.com/search?e=StructuredResults&q=XPath%2BTutorial&num=25">Online XPath Tutorials</a> 

</li>
</ul>
</div>
</div>
<div id="postamble"><p class="author"> Author: T.V. Raman, Charles L. Chen
<a href="mailto:raman@google.com">&lt;raman@google.com&gt;</a>
<a href="mailto:clchen@google.com">&lt;clchen@google.com&gt;</a>
</p>
<p class="date"> Date: 2008/07/03 15:15:54</p>
<p>HTML generated by org-mode 6.05a in emacs 23<p>
</div></body>
</html>
